//#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>

#include "sja1000.h"

//#define CONFIG_SJA_DEBUG
//#define CONFIG_SJA_DEBUG_REG

#ifdef CONFIG_SJA_DEBUG
#define dprintk(msg...)	printk("SJA1000: " msg)
#else
#define dprintk(msg...)
#endif

#ifdef CONFIG_SJA_DEBUG_REG
#define dprintk2(msg...)	printk("SJA1000: " msg)
#else
#define dprintk2(msg...)
#endif

#define SJA_MAJOR               125

#define SJA_REG_BASE            na_SJA1000
#define SJA_REG_SIZE            na_SJA1000_size
#define SJA_IRQ                 na_SJA1000_irq

#define PACKET_SIZE             (sizeof(CAN_PACKAGE) - sizeof(struct list_head))
#define WRITE_TIMEOUT           (HZ * 5)
#define MAX_PACKET_COUNT        64

static volatile u8 *sja_base = NULL;
static wait_queue_head_t rx_wq;
static wait_queue_head_t tx_wq;
static u8 irq_type = 0;
static LIST_HEAD(rx_buffer_list);
static s8 rx_pkt_count = 0;
static spinlock_t rx_buf_lock = SPIN_LOCK_UNLOCKED;
static s8 max_rx_pkt_count = MAX_PACKET_COUNT;
static struct semaphore write_sem;
static struct semaphore read_sem;
static int open_count = 0;

static inline u8 sja_reg_read(u32 reg_offset)
{
	u8 v;
	v = readb((u32)sja_base + reg_offset);
	dprintk2("Reading register at address %x, value = %x\n", reg_offset, v);
	return v;
}

static inline void sja_reg_write(u32 reg_offset, u8 val)
{
	dprintk2("Writing register at address %x, value = %x\n", (u32)reg_offset, (u8)val);
	writeb(val, (u32)sja_base + reg_offset);
	dprintk2("Verifying: value = %x\n", sja_reg_read(reg_offset));
}

static void sja_reset(void)
{
	u8 reg;
	
	disable_irq(SJA_IRQ);
	/* Entering reset mode */
	reg = sja_reg_read(BCAN_CTRL);
	dprintk("Entering reset mode\n");
	while(!(reg & RR_RESET_MODE)) {
		reg |= RR_RESET_MODE;
		sja_reg_write(BCAN_CTRL, reg);
		reg = sja_reg_read(BCAN_CTRL);
	}
	dprintk("Reset mode\n");
	/* Init clock divider register */
	reg = CD_FREQ | CD_CLK_ON | CD_RXINTEN | CD_CBP | CD_MODE_PELI;   /* PeliCAN mode */
	sja_reg_write(COMM_CLK_DIV, reg);
	
	/* Init output control register */
	reg = OC_MODE | OC_POL0 | OC_TN0 | OC_TP0 | OC_POL1 | OC_TN1 | OC_TP1;
	sja_reg_write(COMM_OUTPUT_CTRL, reg);
	
	/* Init bus timing register */
	reg = BT0_BRP | BT0_SJW;
	sja_reg_write(COMM_BUS_TIMING0, reg);
	reg = BT1_TSEG1 | BT1_TSEG2 | BT1_SAM;
	sja_reg_write(COMM_BUS_TIMING1, reg);
	
	/* Init acceptance code & mask registers */
	sja_reg_write(PCAN_ACP_CODE0, 0x0);
	sja_reg_write(PCAN_ACP_CODE1, 0x0);
	sja_reg_write(PCAN_ACP_CODE2, 0x0);
	sja_reg_write(PCAN_ACP_CODE3, 0x0);
	sja_reg_write(PCAN_ACP_MASK0, 0xff);
	sja_reg_write(PCAN_ACP_MASK1, 0xff);
	sja_reg_write(PCAN_ACP_MASK2, 0xff);
	sja_reg_write(PCAN_ACP_MASK3, 0xff);
	
	/* Init interrupt enable register */
	reg = INTR_RI_ENABLE | INTR_TI_ENABLE;   /* Enable RX & TX interrupt */
	sja_reg_write(PCAN_INTR_EN, reg);
	
	/* 
	 * Entering operating mode -> PeliCAN mode as default.
	 * Clear Reset Mode bit, select dual Acceptance Filter Mode,
   * switch off Self Test Mode and Listen Only Mode,
   * clear Sleep Mode (wake up) 
   */
	//reg = sja_reg_read(PCAN_MODE);
	dprintk("Entering operating mode\n");
	do {
		reg = PCAN_MODE_AFM;
		sja_reg_write(PCAN_MODE, reg);
		reg = sja_reg_read(PCAN_MODE);
	} while(reg & RR_RESET_MODE);
  dprintk("Acceptance filter mode is used\n");
  
	enable_irq(SJA_IRQ);
}

irqreturn_t sja_interrupt(int irq, void *dev_id)
{
	CAN_PACKAGE *pkg;
	u8 reg, i;
	
	/* Check what type of interrupt is occured */
	reg = sja_reg_read(PCAN_INTR);
	//dprintk("Read INTR @1: 0x%x\n", reg);
	
	if(reg & PCAN_INTR_RI) {   /* Receive interrupt */
		//dprintk("Receive interrupt\n");
		/* We should handle the buffer overrun interrupt in the near future */
		
		pkg = (CAN_PACKAGE *)kmalloc(sizeof(CAN_PACKAGE), GFP_ATOMIC);
		if(!pkg) {
			printk("Warning: failed to allocate kernel memory for event\n");
			sja_reg_write(PCAN_CMD, PCAN_CMD_RRB);
			return IRQ_HANDLED;
		}
		
		/* Read out all the packets in the RX-FIFO */
		//while((count = sja_reg_read(PCAN_RX_MSG_COUNT)) > 0) {
			//memset(&pkg, 0x0, PACKET_SIZE);
			/* Read frame information register */
			reg = sja_reg_read(PCAN_FRAME_INFO);
			pkg->frm_info.format = (reg >> 7) & 0x01;
			pkg->frm_info.rtr = (reg >> 6) & 0x01;
			pkg->frm_info.dlc = reg & 0x0f;
	
			if(!pkg->frm_info.format) {   /* SFF */
				/* Read SFF frame identifier */
				reg = sja_reg_read(PCAN_SFF_ID1);
				pkg->id = reg;
				pkg->id <<= 8;
				reg = sja_reg_read(PCAN_SFF_ID2);
				pkg->id |= reg;
				pkg->id >>= 5;
				
				//if(pkg->id == 0) {
				//	dprintk("Receive SFF packet with ID = 0\n");
					/* Release receive buffer */
  			//	sja_reg_write(PCAN_CMD, PCAN_CMD_RRB);
  			//	return IRQ_HANDLED;
  			//}
  			
  			dprintk("Receive SFF packet with ID = %x\n", pkg->id);
				
				/* Read SFF frame data[8] */
				for(i = 0; i < 8; i++) {
					pkg->data[i] = sja_reg_read(PCAN_SFF_DATA1 + i);
				}
			}
			else {   /* EFF */
				/* Read EFF frame identifier */
				reg = sja_reg_read(PCAN_EFF_ID1);
				pkg->id = reg;
				pkg->id <<= 8;
				reg = sja_reg_read(PCAN_EFF_ID2);
				pkg->id |= reg;
				pkg->id <<= 8;
				reg = sja_reg_read(PCAN_EFF_ID3);
				pkg->id |= reg;
				pkg->id <<= 8;
				reg = sja_reg_read(PCAN_EFF_ID4);
				pkg->id |= reg;
				pkg->id >>= 3;
				
				/*if(pkg->id == 0) {
					dprintk("Receive EFF packet with ID = 0\n");
					 Release receive buffer 
  				sja_reg_write(PCAN_CMD, PCAN_CMD_RRB);
  				return IRQ_HANDLED;
  			}*/
				
				dprintk("Enqueue EFF packet with ID = %x (Packet count: %d)\n", pkg->id, rx_pkt_count);
				
				/* Read EFF frame data[8] */
				for(i = 0; i < 8; i++) {
					pkg->data[i] = sja_reg_read(PCAN_EFF_DATA1 + i);
				}
			}
		
			if(rx_pkt_count < max_rx_pkt_count) {
				list_add_tail(&pkg->list, &rx_buffer_list);
				++rx_pkt_count;
			}
			else {
				//dprintk("Buffer overlapping\n");
				list_add_tail(&pkg->list, &rx_buffer_list);
				pkg = list_entry(rx_buffer_list.next, CAN_PACKAGE, list);
				list_del(&pkg->list);
				kfree(pkg);
			}
		
			/* Release receive buffer */
  		sja_reg_write(PCAN_CMD, PCAN_CMD_RRB);
  	//}
  	
		/* Wake up the process waiting for reading data in the waitqueue */
		irq_type |= PCAN_INTR_RI;
		wake_up_interruptible(&rx_wq);
	}
	else if(reg & PCAN_INTR_TI) {   /* Transmit interrupt */
		//dprintk("Transmit interrupt\n");
		irq_type |= PCAN_INTR_TI;
		/* Wake up the process waiting for writing data in the waitqueue */
		wake_up_interruptible(&tx_wq);
	}
	else return IRQ_NONE;
	
	return IRQ_HANDLED;
}

int sja_open(struct inode *inode, struct file *filp)
{
	int result;
	//dprintk("Device Opened\n");
	if(open_count == 0) {
		if((result = request_irq(SJA_IRQ, sja_interrupt, SA_INTERRUPT, "SJA1000 Peli-CAN", NULL)) < 0) {
		  printk(KERN_INFO "SJA1000: Can't get assigned irq\n");
		  return result;
		}
		dprintk("SJA1000 interrupt installed on IRQ %d\n", SJA_IRQ);
		
		sja_reset();
		init_MUTEX(&write_sem);
		init_MUTEX(&read_sem);
		init_waitqueue_head(&rx_wq);
		init_waitqueue_head(&tx_wq);
		irq_type = 0;
		rx_pkt_count = 0;
	}
	++open_count;
	
	return 0;
}

int sja_release(struct inode *inode, struct file *filp)
{
	CAN_PACKAGE *pkg;
	//dprintk("Device Released\n");
	if(open_count <= 0) {
		open_count = 0;
		return 0;
	}
	
	--open_count;
	if(open_count == 0) {
		free_irq(SJA_IRQ, NULL);
	
		while(rx_pkt_count > 0) {
			if(list_empty(&rx_buffer_list)) {
				rx_pkt_count = 0;
				printk("Warning: RX buffer list is empty\n");
				break;
			}
			pkg = list_entry(rx_buffer_list.next, CAN_PACKAGE, list);
			list_del(&pkg->list);
			kfree(pkg);
			--rx_pkt_count;	
		}
	}
	return 0;
}

int sja_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
	unsigned long flag;
	int retval = 0;
	CAN_PACKAGE *pkg;
	
	if(down_interruptible(&read_sem))
		return -ERESTARTSYS;
		
	spin_lock_irqsave(&rx_buf_lock, flag);
	if(rx_pkt_count == 0) {
		irq_type &= ~PCAN_INTR_RI;
		spin_unlock_irqrestore(&rx_buf_lock, flag);
		//dprintk("No packet received, waiting ...\n");
		//if(!list_empty(&rx_buffer_list)) {
		//	dprintk("ERROR! Buffer list is not empty!\n");
		//}
		/* Waiting for RX interrupt */
		if(wait_event_interruptible(rx_wq, irq_type & PCAN_INTR_RI)) {
			up(&read_sem);
			return -ERESTARTSYS;
		}
		//dprintk("Wakeup\n");
		spin_lock_irqsave(&rx_buf_lock, flag);
	}

	if(list_empty(&rx_buffer_list)) {
		dprintk("ERROR! Buffer list is empty!\n");
		retval = -EINVAL;
		spin_unlock_irqrestore(&rx_buf_lock, flag);
		goto exit;
	}
	pkg = list_entry(rx_buffer_list.next, CAN_PACKAGE, list);
	--rx_pkt_count;
	list_del(&pkg->list);
 	spin_unlock_irqrestore(&rx_buf_lock, flag);
 	
 	dprintk("Dequeue EFF packet with ID = %x (Packet count: %d)\n", pkg->id, rx_pkt_count);
 	if(copy_to_user((void *)buff, pkg, PACKET_SIZE))
		retval = -EINVAL;
	else
 		retval = PACKET_SIZE;

 	kfree(pkg);
 	
exit:
	up(&read_sem); 	
	return retval;
}

int sja_write(struct file *filp, const char *buff, size_t count, loff_t *offp)
{
	static CAN_PACKAGE pkg;
	u8 reg, i;
	int ret;
	
	if(copy_from_user(&pkg, (void *)buff, PACKET_SIZE)) 
		return(-EINVAL);
	
	if(down_interruptible(&write_sem))
		return -ERESTARTSYS;
		
  /* Write frame information register */
  sja_reg_write(PCAN_FRAME_INFO, *((u8 *)&pkg.frm_info));
  
  if(!pkg.frm_info.format) {   /* SFF */
  	/* Write SFF frame identifier 1 */
  	reg = (u8)((pkg.id & 0x7ff) >> 3);
  	sja_reg_write(PCAN_SFF_ID1, reg);
  	/* Write SFF frame identifier 2 */
  	reg = (u8)((pkg.id & 0x7ff) << 5);
  	sja_reg_write(PCAN_SFF_ID2, reg);
  	/* Write SFF frame data[8] */
  	for(i = 0; i < 8; i++) {
  		sja_reg_write(PCAN_SFF_DATA1 + i, pkg.data[i]);
  	}
  }
  else {   /* EFF */
  	/* Write EFF frame identifier 1 */
  	reg = (u8)((pkg.id & 0x1fffffff) >> 21);
  	sja_reg_write(PCAN_EFF_ID1, reg);
  	/* Write EFF frame identifier 2 */
  	reg = (u8)((pkg.id & 0x1fffffff) >> 13);
  	sja_reg_write(PCAN_EFF_ID2, reg);
  	/* Write EFF frame identifier 3 */
  	reg = (u8)((pkg.id & 0x1fffffff) >> 5);
  	sja_reg_write(PCAN_EFF_ID3, reg);
  	/* Write EFF frame identifier 4 */
  	reg = (u8)((pkg.id & 0x1fffffff) << 3);
  	sja_reg_write(PCAN_EFF_ID4, reg);
  	/* Write EFF frame data[8] */
  	for(i = 0; i < 8; i++) {
  		sja_reg_write(PCAN_EFF_DATA1 + i, pkg.data[i]);
  	}
  }
  
  /* Request transmission */
  sja_reg_write(PCAN_CMD, PCAN_CMD_TR);
  
  /* Waiting for transmission to complete */
  irq_type &= ~PCAN_INTR_TI;
  ret = wait_event_interruptible_timeout(tx_wq, irq_type & PCAN_INTR_TI, WRITE_TIMEOUT);
  if(ret < 0) {  /* Wake up by a signal */
  	ret = -ERESTARTSYS;
  }
  else if(ret == 0)  {
  	/* Transmit timeout, abort packet */
  	sja_reg_write(PCAN_CMD, PCAN_CMD_AT);
  	ret = -ETIMEDOUT;
  }
  else {
  	ret = 0;
  }

  up(&write_sem);
	return ret;
}

int sja_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
	u32 acceptance_code = 0;
	u32 acceptance_mask = ACCEPTANCE_MASK_ALL;
	
	switch (cmd) {
		case SJAIOCTL_ACCEPTCODE_STD: {
			if(copy_from_user(&acceptance_code, (void __user *)arg, sizeof(u32)))
				return -EFAULT;
			
			acceptance_code <<= 5;
			sja_reg_write(PCAN_ACP_CODE1, (u8)(acceptance_code));
			sja_reg_write(PCAN_ACP_CODE0, (u8)(acceptance_code >> 8));
			break;
		}
		case SJAIOCTL_ACCEPTMASK_STD: {
			if(copy_from_user(&acceptance_mask, (void __user *)arg, sizeof(u32)))
				return -EFAULT;
			
			acceptance_mask <<= 5;
			acceptance_mask |= 0x1f;
			sja_reg_write(PCAN_ACP_MASK3, 0xff);
			sja_reg_write(PCAN_ACP_MASK2, 0xff);
			sja_reg_write(PCAN_ACP_MASK1, (u8)(acceptance_mask));
			sja_reg_write(PCAN_ACP_MASK0, (u8)(acceptance_mask >> 8));
			break;
		}
		case SJAIOCTL_ACCEPTCODE_EXT: {
			if(copy_from_user(&acceptance_code, (void __user *)arg, sizeof(u32)))
				return -EFAULT;
			
			acceptance_code <<= 3;
			sja_reg_write(PCAN_ACP_CODE3, (u8)(acceptance_code));
			sja_reg_write(PCAN_ACP_CODE2, (u8)(acceptance_code >> 8));
			sja_reg_write(PCAN_ACP_CODE1, (u8)(acceptance_code >> 16));
			sja_reg_write(PCAN_ACP_CODE0, (u8)(acceptance_code >> 24));
			break;
		}
		case SJAIOCTL_ACCEPTMASK_EXT: {
			if(copy_from_user(&acceptance_mask, (void __user *)arg, sizeof(u32)))
				return -EFAULT;
			
			acceptance_mask <<= 3;
			acceptance_mask |= 0x7;
			sja_reg_write(PCAN_ACP_MASK3, (u8)(acceptance_mask));
			sja_reg_write(PCAN_ACP_MASK2, (u8)(acceptance_mask >> 8));
			sja_reg_write(PCAN_ACP_MASK1, (u8)(acceptance_mask >> 16));
			sja_reg_write(PCAN_ACP_MASK0, (u8)(acceptance_mask >> 24));
			break;
		}
	}
	
	return 0;
}

struct file_operations sja_fops = {
	open:		 sja_open,
	release: sja_release,
	read:    sja_read,
	write:   sja_write,
	ioctl:	 sja_ioctl,
	owner:   THIS_MODULE,
};

static int __init sja_init(void)
{	
	int result;
	
	if (!request_mem_region((unsigned long)SJA_REG_BASE, SJA_REG_SIZE, "SJA1000 Register")) {
		return -EBUSY;
	}
	
	sja_base = (u8 *)ioremap((unsigned long)SJA_REG_BASE, SJA_REG_SIZE);
	if(!sja_base) {
		printk(KERN_WARNING "SJA1000: Remapping device failed\n");
		release_mem_region((unsigned long)SJA_REG_BASE, SJA_REG_SIZE);
		return -1;
	}
	dprintk("Got SJA1000 Register\n");
	
	result = register_chrdev(SJA_MAJOR, "SJA1000 PeliCan", &sja_fops);
	if(result < 0) {
		printk(KERN_WARNING "SJA1000: Can't get major %d\n", SJA_MAJOR);
		iounmap((void *)sja_base);
		release_mem_region((unsigned long)SJA_REG_BASE, SJA_REG_SIZE);
		return result;
	}
	dprintk("SJA1000 driver registered\n");
	
	return 0;
}

static void __exit sja_cleanup(void) 
{
	unregister_chrdev(SJA_MAJOR, "SJA1000 PeliCAN");
	iounmap((void *)sja_base);
	release_mem_region((unsigned long)SJA_REG_BASE, SJA_REG_SIZE);
	printk("SJA1000 PeliCAN bus driver removed!\n");
}

module_init(sja_init);
module_exit(sja_cleanup);

MODULE_AUTHOR("Kenny, Fan");
MODULE_DESCRIPTION("SJA1000 PeliCAN bus driver for the NIOS2 Dashboard System");
MODULE_LICENSE("GPL");
